﻿//////////////////////////////////////////////////////////////////////////////
//
//  Copyright © 1998-2024 Glodon Company Limited.  All rights reserved.
//
//  Use of this software is subject to the terms of the Glodon license
//  agreement provided at the time of installation or download, or which
//  otherwise accompanies this software in either electronic or hard copy form.  
//
//////////////////////////////////////////////////////////////////////////////


#pragma once
#include "NdbPropertySerializeUtils.h"
#include "IDbObjectArchive.h"

namespace gcmp
{
    template<class I, class Enable = void>
    class NdbPropertySerialize
    {
    public:
        static UInt64 Serialize(const I & value, IDbObjectArchive & objectArchive, NdbPropertyUtils::BehavioursAdapter adapter)
        {
            STATIC_ASSERT_FALSE("Please check NdbTypeTraits.h for supported property type.");
            return -1;
        }

        static void CollectModuleGroups(const I & value, std::set<Guid, GuidComparer>& moduleGroupsGuids)
        {
            STATIC_ASSERT_FALSE("Please check NdbTypeTraits.h for supported property type.");
        }
    };

    template<class T>
    class NdbPropertySerialize<T, typename std::enable_if<std::is_pointer<T>::value>::type>
    {
    public:
        static UInt64 Serialize(const T & value, IDbObjectArchive & objectArchive, NdbPropertyUtils::BehavioursAdapter adapter)
        {
            return NdbPropertySerializeUtils::SerializeRawPointer(objectArchive, adapter);
        }

        static void CollectModuleGroups(const T & value, std::set<Guid, GuidComparer>& moduleGroupsGuids)
        {
            return;
        }
    };

    template<class T>
    class NdbPropertySerialize<T, typename std::enable_if<property_is_value_type<T>::value>::type>
    {
    public:
        static UInt64 Serialize(const T & value, IDbObjectArchive & objectArchive, NdbPropertyUtils::BehavioursAdapter adapter)
        {
            NdbPropertySchemaType propertySchemaType = NdbPropertySchemaTypeTrait<T>::PropertySchemaType;
            IntBaseType intBaseType = NdbPropertySchemaTypeTrait<T>::IntBaseType;
            IntOrFloatType intOrFloatType = NdbPropertySchemaTypeTrait<T>::IntOrFloatType;

            std::vector<char> data;
            data.assign((char *)&value, (char *)&value + sizeof(T));

            return NdbPropertySerializeUtils::SerializeValue(
                sizeof(T),
                intBaseType,
                intOrFloatType,
                propertySchemaType,
                data,
                objectArchive,
                adapter
            );
        }

        static void CollectModuleGroups(const T & value, std::set<Guid, GuidComparer>& moduleGroupsGuids)
        {
            return;
        }
    };

    template<class T>
    class NdbPropertySerialize<T, typename std::enable_if<property_is_wstring_type<T>::value>::type>
    {
    public:
        static UInt64 Serialize(const T & value, IDbObjectArchive & objectArchive, NdbPropertyUtils::BehavioursAdapter adapter)
        {
            return NdbPropertySerializeUtils::SerializeWString(value, objectArchive, adapter);
        }

        static void CollectModuleGroups(const T & value, std::set<Guid, GuidComparer>& moduleGroupsGuids)
        {
            return;
        }
    };

    template<typename T>
    class NdbPropertySerialize<T, typename std::enable_if<property_is_elementId_type<T>::value>::type>
    {
    public:
        static UInt64 Serialize(const ElementId & value, IDbObjectArchive & objectArchive, NdbPropertyUtils::BehavioursAdapter adapter)
        {
            NdbPropertySchemaType propertySchemaType = NdbPropertySchemaTypeTrait<T>::PropertySchemaType;
            IntBaseType intBaseType = NdbPropertySchemaTypeTrait<T>::IntBaseType;
            IntOrFloatType intOrFloatType = NdbPropertySchemaTypeTrait<T>::IntOrFloatType;

            return NdbPropertySerializeUtils::SerializeElementId(
                sizeof(Int64),
                intBaseType,
                intOrFloatType,
                propertySchemaType,
                value,
                objectArchive,
                adapter
            );
        }

        static void CollectModuleGroups(const T & value, std::set<Guid, GuidComparer>& moduleGroupsGuids)
        {
            return;
        }
    };

    template<typename T>
    class NdbPropertySerialize<T, typename std::enable_if<property_is_object_schematic_type<T>::value>::type>
    {
    public:
        static UInt64 Serialize(const T & value, IDbObjectArchive & archive, NdbPropertyUtils::BehavioursAdapter adapter)
        {
            if (nullptr == &value)
            {
                return NdbPropertySerialize<T*>::Serialize(static_cast<T*>(nullptr), archive, adapter);
            }
            else
            {
                return NdbPropertySerializeUtils::SerializeSchematicObject(&value, archive, adapter);
            }
        }

        static void CollectModuleGroups(const T & value, std::set<Guid, GuidComparer>& moduleGroupsGuids)
        {
            if (nullptr == &value)
                return;
            NdbDataInterface::CollectModuleGroupsOfNdbObject((const NdbObject*)(&value), moduleGroupsGuids);
        }
    };

    template<typename T>
    class NdbPropertySerialize<T, typename std::enable_if<property_is_object_cloneable_type<T>::value>::type>
    {
    public:
        static UInt64 Serialize(const T & value, IDbObjectArchive & archive, NdbPropertyUtils::BehavioursAdapter adapter)
        {
            if (nullptr == &value)
            {
                return NdbPropertySerialize<T*>::Serialize(static_cast<T*>(nullptr), archive, adapter);
            }
            else
            {
                return NdbPropertySerializeUtils::SerializeCloneableObject(&value, archive, adapter);
            }
        }

        static void CollectModuleGroups(const T & value, std::set<Guid, GuidComparer>& moduleGroupsGuids)
        {
            if (nullptr == &value)
                return;
            NdbDataInterface::CollectModuleGroupsOfNdbObject((const NdbObject*)(&value), moduleGroupsGuids);
        }
    };

    template<typename T>
    class NdbPropertySerialize<T, typename std::enable_if<property_is_idbobject_type<T>::value && !property_is_object_schematic_type<T>::value>::type>
    {
    public:
        static UInt64 Serialize(const T & value, IDbObjectArchive & archive, NdbPropertyUtils::BehavioursAdapter adapter)
        {
            if (nullptr == &value)
            {
                return NdbPropertySerialize<T*>::Serialize(static_cast<T*>(nullptr), archive, adapter);
            }
            else
            {
                return NdbPropertySerializeUtils::SerializeIDbObject(&value, archive, adapter);
            }
        }

        static void CollectModuleGroups(const T & value, std::set<Guid, GuidComparer>& moduleGroupsGuids)
        {
            if (nullptr == &value)
                return;
            NdbDataInterface::CollectModuleGroupsOfNdbObject((const NdbObject*)(&value), moduleGroupsGuids);
        }
    };

    template<class T>
    class NdbPropertySerialize<gcmp::OwnerPtr<T>, typename std::enable_if<property_is_object_schematic_owner_ptr_type<gcmp::OwnerPtr<T>>::value>::type>
    {
    public:
        static UInt64 Serialize(const gcmp::OwnerPtr<T> & value, IDbObjectArchive & archive, NdbPropertyUtils::BehavioursAdapter adapter)
        {
            Int32 headerLen = NdbPropertySerializeUtils::SerializeBlockHeaderPlaceholder(archive);
            UInt64 bodyLen = NdbPropertySerialize<T>::Serialize(*value.get(), archive, adapter);
            return NdbPropertySerializeUtils::FillBackBlockHeader(
                headerLen,
                (int32_t)NdbPropertySchemaType::OwnerPtrToNdbObject,
                bodyLen,
                archive
            );
        }

        static void CollectModuleGroups(const gcmp::OwnerPtr<T> & value, std::set<Guid, GuidComparer>& moduleGroupsGuids)
        {
            NdbPropertySerialize<T>::CollectModuleGroups(*value.get(), moduleGroupsGuids);
        }
    };

    template<class T>
    class NdbPropertySerialize<gcmp::OwnerPtr<T>, typename std::enable_if<property_is_object_cloneable_owner_ptr_type<gcmp::OwnerPtr<T>>::value>::type>
    {
    public:
        static UInt64 Serialize(const gcmp::OwnerPtr<T> & value, IDbObjectArchive & archive, NdbPropertyUtils::BehavioursAdapter adapter)
        {
            if (nullptr == value.get())
            {
                return NdbPropertySerialize<T*>::Serialize(static_cast<T*>(nullptr), archive, adapter);
            }
            else
            {
                Int32 headerLen = NdbPropertySerializeUtils::SerializeBlockHeaderPlaceholder(archive);
                UInt64 bodyLen = NdbPropertySerializeUtils::SerializeCloneableObject(value.get(), archive, adapter);
                return NdbPropertySerializeUtils::FillBackBlockHeader(
                    headerLen,
                    (int32_t)NdbPropertySchemaType::OwnerPtrToNdbObject,
                    bodyLen,
                    archive
                );
            }
        }

        static void CollectModuleGroups(const gcmp::OwnerPtr<T> & value, std::set<Guid, GuidComparer>& moduleGroupsGuids)
        {
            NdbPropertySerialize<T>::CollectModuleGroups(*value.get(), moduleGroupsGuids);
        }
    };

    template<class T>
    class NdbPropertySerialize<gcmp::OwnerPtr<T>, typename std::enable_if<property_is_idbobject_owner_ptr_type<gcmp::OwnerPtr<T>>::value && !property_is_object_schematic_owner_ptr_type<gcmp::OwnerPtr<T>>::value>::type>
    {
    public:
        static UInt64 Serialize(const gcmp::OwnerPtr<T> & value, IDbObjectArchive & archive, NdbPropertyUtils::BehavioursAdapter adapter)
        {
            const IDbObject * pDbObject = value.get();
            if (nullptr == pDbObject)
            {
                return NdbPropertySerialize<T*>::Serialize(static_cast<T*>(nullptr), archive, adapter);
            }
            else
            {
                Int32 headerLen = NdbPropertySerializeUtils::SerializeBlockHeaderPlaceholder(archive);
                UInt64 bodyLen = 0;
                if (const NdbObject * pNdbObject = thread_safe_quick_cast<NdbObject>(pDbObject))
                {
                    const NdbObjectSchematic * pObjectSchematic = thread_safe_quick_cast<NdbObjectSchematic>(pNdbObject);
                    DBG_WARN_AND_RETURN_UNLESS(pObjectSchematic, 0, L"OwnerPtr<IDbObject>指向的对象是NdbObject，但不是NdbObjectSchematic?", L"GMEP", L"2019/3/29");
                    bodyLen = NdbPropertySerialize<NdbObjectSchematic>::Serialize(*pObjectSchematic, archive, adapter);
                }
                else
                {
                    bodyLen = NdbPropertySerialize<T>::Serialize(*value.get(), archive, adapter);
                }
                return NdbPropertySerializeUtils::FillBackBlockHeader(
                    headerLen,
                    (int32_t)NdbPropertySchemaType::OwnerPtrToIDbObject,
                    bodyLen,
                    archive
                );
            }
        }

        static void CollectModuleGroups(const gcmp::OwnerPtr<T> & value, std::set<Guid, GuidComparer>& moduleGroupsGuids)
        {
            const IDbObject * pDbObject = value.get();
            if (nullptr == pDbObject)
            {
                return;
            }
            else
            {
                if (const NdbObject * pNdbObject = thread_safe_quick_cast<NdbObject>(pDbObject))
                {
                    const NdbObjectSchematic * pObjectSchematic = thread_safe_quick_cast<NdbObjectSchematic>(pNdbObject);
                    DBG_WARN_AND_RETURN_VOID_UNLESS(pObjectSchematic, L"OwnerPtr<IDbObject>指向的对象是NdbObject，但不是NdbObjectSchematic?", L"GMEP", L"2019/3/29");
                    NdbPropertySerialize<NdbObjectSchematic>::CollectModuleGroups(*pObjectSchematic, moduleGroupsGuids);
                    return;
                }
                else
                {
                    NdbPropertySerialize<T>::CollectModuleGroups(*value.get(), moduleGroupsGuids);
                    return;
                }
            }
        }
    };

    template<class I, class Enable = void>
    class NdbPropertySerializeWithoutHeader
    {
    public:
        static UInt64 Serialize(const I & value, IDbObjectArchive & objectArchive, NdbPropertyUtils::BehavioursAdapter adapter)
        {
            DBG_WARN( L"非值属性不应该运行到该段代码", L"GMEP", L"2019/3/29");
            return -1;
        }
    };

    template<class T>
    class NdbPropertySerializeWithoutHeader<T, typename std::enable_if<property_is_value_type<T>::value>::type>
    {
    public:
        static UInt64 Serialize(const T & value, IDbObjectArchive & objectArchive, NdbPropertyUtils::BehavioursAdapter adapter)
        {
            std::vector<char> data;
            data.assign((char *)&value, (char *)&value + sizeof(T));
            return NdbPropertySerializeUtils::SerializeValueWithoutHeader(
                sizeof(T),
                data,
                objectArchive
            );
        }
    };
}
